package engine.operators;

import its.util.RandomFactory;

import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import engine.Operator;
import engine.Population;
import engine.individuals.NaturalVectorIndividual;

/**
 * Uniform crossover operator. Offsprings have equal probability of inheriting
 * gene from both of his parents.
 * 
 */
public class ProgramedMeanCrossover implements Operator<NaturalVectorIndividual> {

	/** Head-tail probability. */
	private static final double DEFAULT_PROBABILITY = 0.5;

	/**
	 * Creates a uniform crossover object.
	 */
	public ProgramedMeanCrossover() {
	}

	/** {@inheritDoc} */
	public Population<NaturalVectorIndividual> apply(
			Population<NaturalVectorIndividual> population) {
		List<NaturalVectorIndividual> individuals = population.getIndividuals();
		Collections.shuffle(individuals, RandomFactory.getRandom());

		Iterator<NaturalVectorIndividual> iterator = individuals.iterator();
		List<NaturalVectorIndividual> output = new LinkedList<NaturalVectorIndividual>();

		while (iterator.hasNext()) {
			NaturalVectorIndividual b1 = iterator.next();
			if (iterator.hasNext()) {
				NaturalVectorIndividual b2 = iterator.next();

				NaturalVectorIndividual[] crossedOver = crossOver(b1, b2);

				output.add(crossedOver[0]);
				output.add(crossedOver[1]);
			} else {
				output.add(b1);
			}
		}

		return new Population<NaturalVectorIndividual>(output);
	}

	private NaturalVectorIndividual[] crossOver(NaturalVectorIndividual b1,
			NaturalVectorIndividual b2) {

		NaturalVectorIndividual[] result = new NaturalVectorIndividual[2];

		result[0] = new NaturalVectorIndividual(b1.size());
		result[1] = new NaturalVectorIndividual(b2.size());

		double randRatio = RandomFactory.getNextDouble();
		for (int i = 0; i < b1.size(); i++) {

			long floorCeil = (long) Math.floor(randRatio
					* (double) (b2.get(i) - b1.get(i)));
			long z1 = b1.get(i) + floorCeil;
			long z2 = b2.get(i) + b1.get(i) - z1;

			result[0].set(i, z1);
			result[1].set(i, z2);
		}

		result[0].heal();
		result[1].heal();
		return result;
	}
}
